/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.util.lang;

import lombok.experimental.UtilityClass;

import java.io.Serial;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具类
 *
 * @author zeng
 */
@UtilityClass
public class StringUtils  extends CharSequenceUtils{
  /** 下划线字符 */
  public static final char UNDERLINE = '_';
  /**
   * 空格
   */
  public static final String SPACE = " ";

  public static int INDEX_NOT_FOUND = -1;


  public static boolean isEmpty(CharSequence str) {
    return str == null || str.isEmpty();
  }

  /**
   * 判断是否为空白，为空则返回true
   *
   * @param cs {@link CharSequence}
   * @return 为空则返回true
   */
  public boolean isBlank(final CharSequence cs) {
    if (cs == null) {
      return true;
    }
    int l = cs.length();
    if (l > 0) {
      for (int i = 0; i < l; i++) {
        if (!Character.isWhitespace(cs.charAt(i))) {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Trims the specified string and returns an empty string if the original string is null. This
   * method is designed to avoid NullPointerException when calling the trim operation on a null
   * object.
   *
   * @param str The string to be trimmed
   * @return The trimmed string or an empty string if the original string is null
   */
  public static String trimToEmpty(final String str) {
    // Check if the input string is null, return an empty string if true, otherwise return the
    // trimmed result of the string
    return str == null ? EMPTY : str.trim();
  }

  public boolean isNotBlank(final CharSequence cs) {
    return !isBlank(cs);
  }

  /** 去除换行 */
  static final Pattern REPLACE_BLANK_PATTERN = Pattern.compile("\\s*|\t|\r|\n");

  /**
   * replaceBlank
   *
   * @param str {@link String}
   * @return {@link String}
   */
  public String replaceBlank(String str) {
    String dest = "";
    if (str != null) {
      Matcher m = REPLACE_BLANK_PATTERN.matcher(str);
      dest = m.replaceAll("");
    }
    return dest;
  }

  /**
   * 如果为空默认
   *
   * @param str {@link String}
   * @param defaultStr {@link String}
   * @return {@link String}
   */
  public String defaultString(final String str, final String defaultStr) {
    return isBlank(str) ? defaultStr : str;
  }

  /**
   * 字符串驼峰转下划线格式
   *
   * @param param 需要转换的字符串
   * @return 转换好的字符串
   */
  public static String camelToUnderline(String param) {
    if (isBlank(param)) {
      return EMPTY;
    }
    int len = param.length();
    StringBuilder sb = new StringBuilder(len);
    for (int i = 0; i < len; i++) {
      char c = param.charAt(i);
      if (Character.isUpperCase(c) && i > 0) {
        sb.append(UNDERLINE);
      }
      sb.append(Character.toLowerCase(c));
    }
    return sb.toString();
  }

  /**
   * \ 判断字符串是否为空
   *
   * @param line 需要判断的字符串
   * @return boolean
   */
  public static boolean hasText(String line) {
    return StringUtils.isNotBlank(line);
  }

  public static String str(CharSequence obj) {
    return str(obj, Charset.defaultCharset());
  }

  /**
   * 将对象转为字符串
   *
   * <pre>
   *  1. Byte数组和ByteBuffer会被转换为对应字符串的数组
   *  2. 对象数组会调用Arrays.toString方法
   * </pre>
   *
   * @param obj 对象
   * @param charset 字符集
   * @return 字符串
   */
  public static String str(Object obj, Charset charset) {
    if (null == obj) {
      return null;
    }

    if (obj instanceof String) {
      return (String) obj;
    } else if (obj instanceof byte[]) {
      return str((byte[]) obj, charset);
    } else if (obj instanceof Byte[]) {
      return str((Byte[]) obj, charset);
    } else if (obj instanceof ByteBuffer) {
      return str((ByteBuffer) obj, charset);
    } else if (ArrayUtils.isArray(obj)) {
      return ArrayUtils.toString(obj);
    }

    return obj.toString();
  }

  public static boolean contains(String name2, char symbol) {
    return name2 != null && name2.indexOf(symbol) > 0;
  }

  /**
   * 大写首字母<br>
   * 例如：str = name, return Name
   *
   * @param str 字符串
   * @return 字符串
   */
  public static String upperFirst(CharSequence str) {
    if (null == str) {
      return null;
    }
    if (str.length() > 0) {
      char firstChar = str.charAt(0);
      if (Character.isLowerCase(firstChar)) {
        return Character.toUpperCase(firstChar) + subSuf(str, 1);
      }
    }
    return str.toString();
  }

  /**
   * 切割指定位置之后部分的字符串
   *
   * @param string 字符串
   * @param fromIndex 切割开始的位置（包括）
   * @return 切割后后剩余的后半部分字符串
   */
  public static String subSuf(CharSequence string, int fromIndex) {
    if (isEmpty(string)) {
      return null;
    }
    return sub(string, fromIndex, string.length());
  }

  /**
   * 改进JDK subString<br>
   * index从0开始计算，最后一个字符为-1<br>
   * 如果from和to位置一样，返回 "" <br>
   * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
   * 如果经过修正的index中from大于to，则互换from和to example: <br>
   * abcdefgh 2 3 =》 c <br>
   * abcdefgh 2 -3 =》 cde <br>
   *
   * @param str String
   * @param fromIndexInclude 开始的index（包括）
   * @param toIndexExclude 结束的index（不包括）
   * @return 字串
   */
  public static String sub(CharSequence str, int fromIndexInclude, int toIndexExclude) {
    if (isEmpty(str)) {
      return str(str);
    }
    int len = str.length();

    if (fromIndexInclude < 0) {
      fromIndexInclude = len + fromIndexInclude;
      if (fromIndexInclude < 0) {
        fromIndexInclude = 0;
      }
    } else if (fromIndexInclude > len) {
      fromIndexInclude = len;
    }

    if (toIndexExclude < 0) {
      toIndexExclude = len + toIndexExclude;
      if (toIndexExclude < 0) {
        toIndexExclude = len;
      }
    } else if (toIndexExclude > len) {
      toIndexExclude = len;
    }

    if (toIndexExclude < fromIndexInclude) {
      int tmp = fromIndexInclude;
      fromIndexInclude = toIndexExclude;
      toIndexExclude = tmp;
    }

    if (fromIndexInclude == toIndexExclude) {
      return EMPTY;
    }

    return str.toString().substring(fromIndexInclude, toIndexExclude);
  }

  /**
   * 小写首字母<br>
   * 例如：str = Name, return name
   *
   * @param str 字符串
   * @return 字符串
   */
  public static String lowerFirst(CharSequence str) {
    if (null == str) {
      return null;
    }
    if (!str.isEmpty()) {
      char firstChar = str.charAt(0);
      if (Character.isUpperCase(firstChar)) {
        return Character.toLowerCase(firstChar) + subSuf(str, 1);
      }
    }
    return str.toString();
  }

  /**
   * 截取分隔字符串之后的字符串，不包括分隔字符串<br>
   * 如果给定的字符串为空串（null或""），返回原字符串<br>
   * 如果分隔字符串为空串（null或""），则返回空串，如果分隔字符串未找到，返回空串，举例如下：
   *
   * <pre>
   * .subAfter(null, *, false)      = null
   * .subAfter("", *, false)        = ""
   * .subAfter(*, null, false)      = ""
   * .subAfter("abc", "a", false)   = "bc"
   * .subAfter("abcba", "b", false) = "cba"
   * .subAfter("abc", "c", false)   = ""
   * .subAfter("abc", "d", false)   = ""
   * .subAfter("abc", "", false)    = "abc"
   * </pre>
   *
   * @param string 被查找的字符串
   * @param separator 分隔字符串（不包括）
   * @param isLastSeparator 是否查找最后一个分隔字符串（多次出现分隔字符串时选取最后一个），true为选取最后一个
   * @return 切割后的字符串
   * @since 3.1.1
   */
  public static String subAfter(
      CharSequence string, CharSequence separator, boolean isLastSeparator) {
    if (isEmpty(string)) {
      return null == string ? null : EMPTY;
    }
    if (separator == null) {
      return EMPTY;
    }
    final String str = string.toString();
    final String sep = separator.toString();
    final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
    if (INDEX_NOT_FOUND == pos || (string.length() - 1) == pos) {
      return EMPTY;
    }
    return str.substring(pos + separator.length());
  }

  /**
   * 是否包含特定字符，忽略大小写，如果给定两个参数都为{@code null}，返回true
   *
   * @param str 被检测字符串
   * @param testStr 被测试是否包含的字符串
   * @return 是否包含
   */
  public static boolean containsIgnoreCase(CharSequence str, CharSequence testStr) {
    if (null == str) {
      // 如果被监测字符串和
      return null == testStr;
    }
    return indexOfIgnoreCase(str, testStr) > -1;
  }

  /**
   * 指定范围内查找字符串，忽略大小写<br>
   *
   * <pre>
   * .indexOfIgnoreCase(null, *, *)          = -1
   * .indexOfIgnoreCase(*, null, *)          = -1
   * .indexOfIgnoreCase("", "", 0)           = 0
   * .indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
   * .indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
   * .indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
   * .indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
   * .indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
   * .indexOfIgnoreCase("aabaabaa", "B", -1) = 2
   * .indexOfIgnoreCase("aabaabaa", "", 2)   = 2
   * .indexOfIgnoreCase("abc", "", 9)        = -1
   * </pre>
   *
   * @param str 字符串
   * @param searchStr 需要查找位置的字符串
   */
  public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
    return indexOfIgnoreCase(str, searchStr, 0);
  }

  /**
   * 指定范围内查找字符串
   *
   * <pre>
   * .indexOfIgnoreCase(null, *, *)          = -1
   * .indexOfIgnoreCase(*, null, *)          = -1
   * .indexOfIgnoreCase("", "", 0)           = 0
   * .indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
   * .indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
   * .indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
   * .indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
   * .indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
   * .indexOfIgnoreCase("aabaabaa", "B", -1) = 2
   * .indexOfIgnoreCase("aabaabaa", "", 2)   = 2
   * .indexOfIgnoreCase("abc", "", 9)        = -1
   * </pre>
   *
   * @param str 字符串
   * @param searchStr 需要查找位置的字符串
   * @param fromIndex 起始位置
   */
  public static int indexOfIgnoreCase(
      final CharSequence str, final CharSequence searchStr, int fromIndex) {
    return indexOf(str, searchStr, fromIndex, true);
  }

  /**
   * 指定范围内查找字符串
   *
   * @param text 字符串，空则返回-1
   * @param searchStr 需要查找位置的字符串，空则返回-1
   * @param from 起始位置（包含）
   * @param ignoreCase 是否忽略大小写
   */
  public static int indexOf(
      CharSequence text, CharSequence searchStr, int from, boolean ignoreCase) {
    if (isEmpty(text) || isEmpty(searchStr)) {
      if (Objects.equals(text, searchStr)) {
        return 0;
      } else {
        return INDEX_NOT_FOUND;
      }
    }
    return new StrFinder(searchStr, ignoreCase).setText(text).start(from);
  }

  /**
   * 截取第一个字串的部分字符，与第二个字符串比较（长度一致），判断截取的子串是否相同<br>
   * 任意一个字符串为null返回false
   *
   * @param str1 第一个字符串
   * @param start1 第一个字符串开始的位置
   * @param str2 第二个字符串
   * @param ignoreCase 是否忽略大小写
   * @return 子串是否相同
   * @since 3.2.1
   */
  public static boolean isSubEquals(
      CharSequence str1, int start1, CharSequence str2, boolean ignoreCase) {
    return isSubEquals(str1, start1, str2, 0, str2.length(), ignoreCase);
  }

  /**
   * 截取两个字符串的不同部分（长度一致），判断截取的子串是否相同<br>
   * 任意一个字符串为null返回false
   *
   * @param str1 第一个字符串
   * @param start1 第一个字符串开始的位置
   * @param str2 第二个字符串
   * @param start2 第二个字符串开始的位置
   * @param length 截取长度
   * @param ignoreCase 是否忽略大小写
   */
  public static boolean isSubEquals(
      CharSequence str1,
      int start1,
      CharSequence str2,
      int start2,
      int length,
      boolean ignoreCase) {
    if (null == str1 || null == str2) {
      return false;
    }
    return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length);
  }

  /**
   * 字符串查找器
   *
   * @author zeng
   */
  public static class StrFinder implements Serializable {
    @Serial private static final long serialVersionUID = 1L;

    private final CharSequence strToFind;
    private final boolean caseInsensitive;
    protected CharSequence text;
    protected int endIndex = -1;
    protected boolean negative;

    /**
     * 构造
     *
     * @param strToFind 被查找的字符串
     * @param caseInsensitive 是否忽略大小写
     */
    public StrFinder(CharSequence strToFind, boolean caseInsensitive) {
      Assert.notEmpty(strToFind, "Str to find must be not null!");
      this.strToFind = strToFind;
      this.caseInsensitive = caseInsensitive;
    }

    /**
     * 设置被查找的文本
     *
     * @param text 文本
     * @return this
     */
    public StrFinder setText(CharSequence text) {
      Assert.notNull(text, "Text must be not null!");
      this.text = text;
      return this;
    }

    /**
     * 从指定位置开始查找匹配
     *
     * @param from 开始位置
     * @return int
     */
    public int start(int from) {
      Assert.notNull(this.text, "Text to find must be not null!");
      final int subLen = strToFind.length();

      if (from < 0) {
        from = 0;
      }
      int endLimit = getValidEndIndex();
      if (negative) {
        for (int i = from; i > endLimit; i--) {

          if (isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) {
            return i;
          }
        }
      } else {
        endLimit = endLimit - subLen + 1;
        for (int i = from; i < endLimit; i++) {
          if (isSubEquals(text, i, strToFind, 0, subLen, caseInsensitive)) {
            return i;
          }
        }
      }

      return INDEX_NOT_FOUND;
    }

    /**
     * 获取有效结束位置<br>
     * 如果{@link #endIndex}小于0，在反向模式下是开头（-1），正向模式是结尾（text.length()）
     *
     * @return 有效结束位置
     */
    protected int getValidEndIndex() {
      if (negative && -1 == endIndex) {
        // 反向查找模式下，-1表示0前面的位置，即字符串反向末尾的位置
        return -1;
      }
      final int limit;
      if (endIndex < 0) {
        limit = endIndex + text.length() + 1;
      } else {
        limit = Math.min(endIndex, text.length());
      }
      return limit;
    }

    /**
     * 获取结束位置
     *
     * @param start 开始位置
     * @return int
     */
    public int end(int start) {
      if (start < 0) {
        return -1;
      }
      return start + strToFind.length();
    }
  }

  /**
   * 截取字符串
   *
   * @param str 字符串
   * @param start 开始
   * @param end 结束
   * @return 结果
   */
  public static String substring(final String str, int start, int end) {
    if (str == null) {
      return EMPTY;
    }

    if (end < 0) {
      end = str.length() + end;
    }
    if (start < 0) {
      start = str.length() + start;
    }

    if (end > str.length()) {
      end = str.length();
    }

    if (start > end) {
      return EMPTY;
    }

    if (start < 0) {
      start = 0;
    }
    if (end < 0) {
      end = 0;
    }

    return str.substring(start, end);
  }
}
